#include <strmif.h>
#include "cfactory.hpp"
#include "comreg.hpp"
#include <cassert>
#include <comdef.h>
#include <uuids.h>
#include "graphutil.hpp"

HMODULE g_hModule;
static ULONG s_cLock;


namespace MkvSplit
{
    extern const CLSID CLSID_MkvSplit;
    extern const GUID MEDIASUBTYPE_MKV;

    HRESULT CreateInstance(
            IClassFactory*,
            IUnknown*, 
            const IID&, 
            void**);

}  //end namespace MkvSplit


static MJH::CFactory s_factory(&s_cLock, &MkvSplit::CreateInstance);


BOOL APIENTRY DllMain(
    HINSTANCE hModule, 
    DWORD  dwReason, 
    LPVOID)
{
   switch (dwReason)
   {
       case DLL_PROCESS_ATTACH:
        {
            g_hModule = hModule;
           break;
        }
       case DLL_THREAD_ATTACH:
       case DLL_THREAD_DETACH:
       case DLL_PROCESS_DETACH:
       default:
          break;
   }
   
    return TRUE;
}



STDAPI DllCanUnloadNow()
{
    return s_cLock ? S_FALSE : S_OK;
}


STDAPI DllGetClassObject(
    const CLSID& clsid,
    const IID& iid,
    void** ppv)
{
    if (clsid == MkvSplit::CLSID_MkvSplit)
        return s_factory.QueryInterface(iid, ppv);

    return CLASS_E_CLASSNOTAVAILABLE;
}


STDAPI DllUnregisterServer()
{
    const GraphUtil::IFilterMapper2Ptr pMapper(CLSID_FilterMapper2);
    assert(bool(pMapper));

    HRESULT hr = pMapper->UnregisterFilter(
                    &CLSID_LegacyAmFilterCategory,
                    0,
                    MkvSplit::CLSID_MkvSplit);
                    
    //assert(SUCCEEDED(hr));

    hr = ComReg::UnRegisterCustomFileType(L".mkv", MkvSplit::CLSID_MkvSplit);
    assert(SUCCEEDED(hr));

    hr = ComReg::UnRegisterCoclass(MkvSplit::CLSID_MkvSplit);

    return SUCCEEDED(hr) ? S_OK : S_FALSE;
}


STDAPI DllRegisterServer()
{
    std::wstring filename_;

    HRESULT hr = ComReg::ComRegGetModuleFileName(g_hModule, filename_);
    assert(SUCCEEDED(hr));
    assert(!filename_.empty());
    
    const wchar_t* const filename = filename_.c_str();

#if _DEBUG    
    const wchar_t friendlyname[] = L"MJH MKV Splitter Filter (Debug)";
#else
    const wchar_t friendlyname[] = L"MJH MKV Splitter Filter";
#endif

    hr = DllUnregisterServer();
    assert(SUCCEEDED(hr));
    
    hr = ComReg::RegisterCoclass(
            MkvSplit::CLSID_MkvSplit,
            friendlyname,
            filename,
            L"MJH.MkvSplit",
            L"MJH.MkvSplit.1",
            false,  //not insertable
            false,  //not a control
            ComReg::kBoth,  //DShow filters must support "both"
            GUID_NULL,     //typelib
            0,    //no version specified
            0);   //no toolbox bitmap
            
    assert(SUCCEEDED(hr));
            
#if 1
    //TODO: I thought a .reg file would be adequate to do this, 
    //but for some reason none of GraphEdt, GraphStudio, WMP, etc
    //load this source filter when asked to render a .MKV file.
    //I don't understand why not, because the registry entry is 
    //present.  (Does Win7 do something different?) Does using
    //regsvr32.exe to register a custom file type programmatically
    //sprinkle some pixie dust in the registry, that merging a .reg 
    //file from the command-line or shell does not?
    //
    //I would prefer not to take over a file extension, because this 
    //will clobber any existing registration (of a source filter for
    //the .MKV file extension).  Indeed I would prefer to not have to
    //register a custom file type at all.  (Otherwise what's the point
    //of calling FilterMappter->RegisterFile?)  But I cannot get the 
    //.MKV file to load unless I register this as a source filter, and 
    //only by registering the source filter here, in DllRegisterServer.  
    //I don't know why.
    
    hr = ComReg::RegisterCustomFileType(
            L".mkv",
            MkvSplit::CLSID_MkvSplit,
            MEDIATYPE_Stream,
            MkvSplit::MEDIASUBTYPE_MKV);  //TODO: see note above
                
    assert(SUCCEEDED(hr));
#endif

    const GraphUtil::IFilterMapper2Ptr pMapper(CLSID_FilterMapper2);
    assert(bool(pMapper));
    
    enum { cPins = 2 };
    REGFILTERPINS pins[cPins];
    
    REGFILTERPINS& inpin = pins[0];
    
    enum { nInpinMediaTypes = 1 };
    const REGPINTYPES inpinMediaTypes[nInpinMediaTypes] =
    {
        { &MEDIATYPE_Stream, &MkvSplit::MEDIASUBTYPE_MKV }  //TODO: see above
    };

    inpin.strName = 0;              //obsolete
    inpin.bRendered = FALSE;        //TODO: should this be TRUE?
    inpin.bOutput = FALSE;
    inpin.bZero = FALSE;
    inpin.bMany = FALSE;
    inpin.clsConnectsToFilter = 0;  //obsolete
    inpin.strConnectsToPin = 0;     //obsolete    
    inpin.nMediaTypes = nInpinMediaTypes;
    inpin.lpMediaType = inpinMediaTypes;

    REGFILTERPINS& outpin = pins[1];

    outpin.strName = 0;              //obsolete
    outpin.bRendered = FALSE;        //always FALSE for outpins
    outpin.bOutput = TRUE;
    outpin.bZero = FALSE;            //?
    outpin.bMany = TRUE;
    outpin.clsConnectsToFilter = 0;  //obsolete
    outpin.strConnectsToPin = 0;     //obsolete    
    outpin.nMediaTypes = 0;
    outpin.lpMediaType = 0;

    //pin setup complete
    
    REGFILTER2 filter;
    
    filter.dwVersion = 1;
    filter.dwMerit = MERIT_NORMAL;
    filter.cPins = cPins;
    filter.rgPins = pins;
    
    hr = pMapper->RegisterFilter(
            MkvSplit::CLSID_MkvSplit,
            friendlyname,
            0,
            &CLSID_LegacyAmFilterCategory,
            0,
            &filter);                       

    return hr;
}
